﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartRoute.Data;
using SmartRoute.Randomness;

namespace SmartRoute.Analytics

{
    class Genetics
    {
        //list of the potential stops
        List<Stop> potentialStops;

        //number of stops
        int numberStops;

        //maximum number of buses
        int numberBuses;

        //chance that stop order will change for a chromosome
        double stopPermutationMutateChance;

        //chance that a stop ID will change
        double idMutateChance;

        //source of randomness
        Randomness.RandomSource rand;

        //source of routing information
        CachedRoutingService routingData;

        //indicated wether there will be a set number of buses
        bool fixedNumberBuses;

        //CONSTRUCTOR for entire GenePool
        public Genetics(List<Stop> potentialStops, int numStops, int numBuses, bool fixedBuses, CachedRoutingService routingData)
        {
            this.potentialStops = potentialStops;
            this.numberStops = numStops;
            this.numberBuses = numBuses;
            this.fixedNumberBuses = fixedBuses;
            this.routingData = routingData;
        }


        //converts the chromosome to a complete busSolution that contains routes and passengers on each bus
        public BusSolution convertChromosomeToSolution(Chromosome toDecode)
        {
            //Method returns decoded Bus Solution
            BusSolution decodedSolution = new BusSolution();

            //List of concatenated routes, one for each bus
            List<Route> concatenatedRoutes = new List<Route>();

            //List indicating number of passengers on each bus
            List<int> passengersOnEachBus = new List<int>();

            //indicates the time each bus goes before picking up first passenger
            List<TimeSpan> firstLegNoPassengers = new List<TimeSpan>();

            //loops through the number of buses to reconstruct the route
            for (int i = 0; i < numberBuses; i++)
            {

                //boolean indicates if the first leg was calculated
                bool calculatedFirstLeg = false;

                //initial timespan of 0 for the first leg, will stay the same if bus is not used
                TimeSpan firstLeg = new TimeSpan(0, 0, 0);

                //initial route returned for each bus, idles in place at the depot
                Route beginning = Route.IdleInPlace(toDecode.Stops[0], TimeSpan.Zero, routingData);
                Route nextLeg = Route.IdleInPlace(toDecode.Stops[0], TimeSpan.Zero, routingData);

                //starting at the depot
                Stop lastStopProccessed = toDecode.Depot;
                Stop nextStop;

                int passengers = 0;

                //loops through the permutation of stops and totals the distances and passengers for a specific bus ID
                for (int j = 0; j < numberStops; j++)
                {
                    //checks to see if the current bus goes to any of the stops
                    if (toDecode.BusId[j] == i)
                    {
                        //sets the next stop the bus will goto
                        nextStop = toDecode.Stops[j];
                        //calculates the route using the last stop visited
                        nextLeg = routingData.FromTo(lastStopProccessed, nextStop);
                        //sets the current stop to be the last stop visited
                        lastStopProccessed = nextStop;
                        //concatenates the route
                        Route.Concatenate(beginning, nextLeg);
                        //increments the passengers for that bus
                        passengers += nextStop.Passengers;
                        //sets the first lef without passengers if it has not been set yet
                        if (calculatedFirstLeg == false)
                        {
                            calculatedFirstLeg = true;
                            firstLeg = nextLeg.Duration;
                        }
                    }

                    
                }
                //bus returns to school
                nextStop = toDecode.School;
                nextLeg = routingData.FromTo(lastStopProccessed, nextStop);
                Route.Concatenate(beginning, nextLeg);

                //add the current bus information
                concatenatedRoutes.Add(beginning);
                passengersOnEachBus.Add(passengers);
                firstLegNoPassengers.Add(firstLeg);

            }

            //add bus information to solution object
            decodedSolution.Routes = concatenatedRoutes;
            decodedSolution.Passengers = passengersOnEachBus;
            decodedSolution.FirstLegWithoutPassengersDistance = firstLegNoPassengers;
            
            
            //return solution
            return decodedSolution;
        }

        //holds the encoded bus solutions
        public class Chromosome
        {
            //holds the permutations of stops for this chromosome
            List<Stop> stops;

            public List<Stop> Stops
            {
                get { return stops; }
                set { stops = value; }
            }
            //bus ID at each stop
            List<int> busId;

            public List<int> BusId
            {
                get { return busId; }
                set { busId = value; }
            }

            //starting location
            Stop depot;

            public Stop Depot
            {
                get { return depot; }
                set { depot = value; }
            }

            //ending location
            Stop school;

            public Stop School
            {
                get { return school; }
                set { school = value; }
            }

            int numBuses;
            bool fixedNumberBuses;

            //constructor/initializer for a new chromosome for this population
            public Chromosome(List<Stop> stopOrder, Stop depot, Stop school, RandomSource rand, bool fixedNumBuses, int numBuses)
            {
                
                //cloned list of the potential stops
                Stop[] stops = new Stop[stopOrder.Count];
                //indicates the start and stop for all buses
                this.depot = depot;
                this.school = school;
                this.numBuses = numBuses;
                this.fixedNumberBuses = fixedNumBuses;
                //makes shallow copy of the potential sotps
                stopOrder.CopyTo(stops, 0);
                //shuffle the stops to create first permutation
                rand.shuffle(stops);

                //randomly choses bus IDs for each stop - WITHOUT fixedNumBuses
                if(fixedNumBuses != true){
                    for (int i = 0; i < stops.Length; i++)
                    {
                        busId.Add(rand.fromTo(0,stops.Length-1));
                    }
                }
                //If they require that there is a fixed number of buses part of the stop permutation
                //is set aside.
                else
                {
                    for(int i = 0; i < numBuses; i++)
                    {
                        busId.Add(i);
                    }
                    for(int i = numBuses; i < stops.Length; i++)
                    {
                        busId.Add(rand.fromTo(0,numBuses));
                    }
                }
                

            }

            //method that mutates the permutation
            //NEED TODO:  Shuffle bus ID along with stops
            public void mutatePermutation(RandomSource rand, int mutateChance)
            {
                int willMutate = rand.fromTo(0, 99);
                if (willMutate < mutateChance)
                {
                    rand.randomSwap(this.Stops.ToArray<Stop>());
                }
                List<Stop> tmp = this.Stops.ToList<Stop>();
                this.Stops = tmp;
            }

            //
            public void mutateId(RandomSource rand, int mutateChance)
            {

                int willMutate = rand.fromTo(0, 99);
                if (willMutate < mutateChance)
                {
                    int nextPos = 0;
                    if (this.fixedNumberBuses == false)
                    {
                        nextPos = rand.fromTo(0, this.Stops.Count);
                    }
                    else
                    {
                        nextPos = rand.fromTo(numBuses, this.Stops.Count);
                    }
                    int nextId = rand.fromTo(0, this.numBuses);
                    this.BusId[nextPos] = nextId;
                }
            }
        }
    }

    
}
